package com.na.game.engine.opengl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.Rect;

public class GLTexture {

	protected boolean binded;
	protected int textureID;
	protected int width, height;
	protected List<GLImage> pendingImages = new ArrayList<GLImage>();
	protected List<int[]> pos = new ArrayList<int[]>();
	protected Map<GLImage, int[]> image2pos = new HashMap<GLImage, int[]>();
	protected List<Rect> usedAreas = new ArrayList<Rect>();
	protected List<Rect> remainAreas = new ArrayList<Rect>(); // 最多两个
	protected Map<GLImage, GLFloatBuffer> texturePointerBuffers = new HashMap<GLImage, GLFloatBuffer>();
	
	protected GLTexture() {
		this(GLTextureManager.DEFAULT_TEXTURE_WIDTH, GLTextureManager.DEFAULT_TEXTURE_HEIGHT);
	}
	
	protected GLTexture(int width, int height) {
		this.width = width;
		this.height = height;
		remainAreas.add(new Rect(0, 0, width, height));
	}
	
	protected boolean register(GLImage image) {
		for (int i = 0; i < remainAreas.size(); i++) {
			Rect area = remainAreas.get(i);
			if (area.width() >= image.getWidth() && area.height() >= image.getHeight()) {
				synchronized (pendingImages) {
					pendingImages.add(image);
					int[] p = new int[]{area.left, area.top};
					pos.add(p);
					generateTexturePointer(image, p);
					image2pos.put(image, p);
					usedAreas.add(new Rect(area.left, area.top, area.left + image.getWidth(), area.top + image.getHeight()));
					mergeAreas();
				}
				return true;
			}
		}
		return false;
	}
	
	private void generateTexturePointer(GLImage image, int[] pos) {
		int l = pos[0];
		int t = pos[1];
		int r = l + image.getWidth();
		int b = t + image.getHeight();
		int left = Float.floatToRawIntBits(l / (float) width);
		int top = Float.floatToRawIntBits(t / (float) height);
		int right = Float.floatToRawIntBits(r / (float) width);
		int bottom = Float.floatToRawIntBits(b / (float) height);
		int[] buffer = GLGraphics.texturePointsBuffer;
		int i = 0;
		buffer[i++] = left;
		buffer[i++] = top; // 左上
		buffer[i++] = left;
		buffer[i++] = bottom; // 左下
		buffer[i++] = right;
		buffer[i++] = bottom; // 右下
		buffer[i++] = right;
		buffer[i++] = bottom; // 右下
		buffer[i++] = right;
		buffer[i++] = top; // 右上
		buffer[i++] = left;
		buffer[i++] = top; // 左上
		GLFloatBuffer texture = GLBufferManager.allocateFloatBuffer(i);
		texture.setRecyclable(false); // avoid be recycled in GLGraphics clear
		texture.put(buffer, 0, i);
		
		texturePointerBuffers.put(image, texture);
	}
	
	/**
	 * TODO: ...
	 */
	private void mergeAreas() {
		int x = 0, y = 0;
		for (int i = usedAreas.size() - 1; i >= 0; i--) {
			Rect used = usedAreas.get(i);
			if (used.right > x) {
				x = used.right + 1;
			}
			if (used.bottom > y) {
				y = used.bottom + 1;
			}
		}
		
		remainAreas.clear();
		Rect right = new Rect(x, 0, width, height);
		if (!right.isEmpty())
			remainAreas.add(right);
		Rect bottom = new Rect(0, y, x, height);
		if (!bottom.isEmpty())
			remainAreas.add(bottom);
	}
	
	protected void bind(GL10 gl) {
		if (binded) {
			gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
		} else {
			// 注册材质
	   	 	int[] ids = new int[1];
	   	 	gl.glGenTextures(1, ids, 0);
	   	 	textureID = ids[0];
	   	 	
	   	 	// 设置材质参数
		   	gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
		   	gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
		   	gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
	   	 
	   	    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
	   	    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);
	   	    
	   	    // 设置材质图片
	   	    gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA, width, height, 0, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, null);
//	   	    GLUtils.texSubImage2D(GL10.GL_TEXTURE_2D, 0, 0, 0, bitmap, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE);
//	   	    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
	   	    binded = true;
		}
		
		synchronized (pendingImages) {
			int size = pendingImages.size();
			if (size == 0) {
				return;
			}
			for (int i = 0; i < size; i++) {
				int[] position = pos.get(i);
				pendingImages.get(i).bind(gl, position[0], position[1]);
			}
			pendingImages.clear();
			pos.clear();
		}
	}
	
	protected GLFloatBuffer getTexturePointer(GLImage image) {
		return texturePointerBuffers.get(image);
	}
	
	protected int[] getImagePos(GLImage image) {
		return image2pos.get(image);
	}
	
	protected void clear() {
		synchronized (pendingImages) {
			pendingImages.clear();
			pos.clear();
		}
		Iterator<GLImage> imgs = image2pos.keySet().iterator();
		while (imgs.hasNext()) {
			imgs.next().destroy();
		}
		image2pos.clear();
		usedAreas.clear();
		remainAreas.clear();
		remainAreas.add(new Rect(0, 0, width, height));
		Iterator<GLFloatBuffer> pointers = texturePointerBuffers.values().iterator();
		while (pointers.hasNext()) {
			GLFloatBuffer p = pointers.next();
			p.setRecyclable(true);
			GLBufferManager.release(p);
		}
		texturePointerBuffers.clear();
	}
	
	protected void destroy(GL10 gl) {
		clear();
		if (binded) {
			binded = false;
			gl.glDeleteTextures(1, new int[]{textureID}, 0);
		}
	}
}
